package com.ie.cloud.payment.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.icbc.api.response.RefundResponseV1;
import com.ie.cloud.payment.cache.RedisUtil;
import com.ie.cloud.payment.constants.BizStatus;
import com.ie.cloud.payment.constants.CS;
import com.ie.cloud.payment.dto.GeneralOrderDto;
import com.ie.cloud.payment.dto.RefundOrderAdd;
import com.ie.cloud.payment.entity.MchApp;
import com.ie.cloud.payment.entity.PayOrder;
import com.ie.cloud.payment.entity.PayOrderFlowRecord;
import com.ie.cloud.payment.entity.RefundOrder;
import com.ie.cloud.payment.exception.BizException;
import com.ie.cloud.payment.mapper.MchAppMapper;
import com.ie.cloud.payment.mapper.RefundOrderMapper;
import com.ie.cloud.payment.request.MyEPaymentRefundRequestV1;
import com.ie.cloud.payment.utils.IdUtils;
import com.ie.cloud.payment.utils.StringKit;
import com.ie.cloud.payment.vo.base.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * Created with FR
 *
 * @Author: DQ
 * @Date: 2025/07/21/16:02
 * @Description: 描述语句
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class RefundOrderServiceImpl {

    public static final int ORDER_EXPIRED_TIME_OF_SECOND = 30 * 60;

    @Resource
    PayOrderService payOrderService;

    @Resource
    private PayOrderFlowRecordServiceImpl payOrderFlowRecordService;

    @Resource
    private ICBCServiceImpl icbcService;

    @Resource
    RefundOrderMapper refundOrderMapper;

    @Resource
    MchAppMapper mchAppMapper;


    /**
     * 退款订单生成
     */
    public Result<JSONObject> refundOrder(RefundOrderAdd rq) {

        //业务类型(租户计费)+业务编号(账单号)+创建时间戳  ===> 商户退款账单号号
        StringBuilder bizBill = new StringBuilder()
                .append(CS.PREFIX_CODE.REFUND_PREFIX)
                .append(rq.getBizType())
                .append(rq.getBizCode());

        //业务类型(租户计费)+业务编号(账单号)+创建时间戳  ===> 商户账单号
        StringBuilder bizBillMchOrderId = new StringBuilder()
                .append(CS.PREFIX_CODE.ORDER_PREFIX)
                .append(rq.getBizType())
                .append(rq.getBizCode());



        rq.setMchRefundNo(bizBillMchOrderId.toString());

        try {

            if(StringUtils.isAllEmpty(rq.getMchOrderNo(), rq.getPayOrderId())){
                throw new BizException("mchOrderNo 和 payOrderId不能同时为空");
            }

            if(StringUtils.isNotEmpty(rq.getNotifyUrl()) && !StringKit.isAvailableUrl(rq.getNotifyUrl())){
                throw new BizException("异步通知地址协议仅支持http:// 或 https:// !");
            }

            PayOrder payOrder = payOrderService.queryMchOrder(rq.getPayOrderId(), bizBillMchOrderId.toString());
            if(payOrder == null){
                throw new BizException("退款订单不存在");
            }

            if(payOrder.getState() != PayOrder.STATE_SUCCESS){
                throw new BizException("订单状态不正确， 无法完成退款");
            }

            if(payOrder.getRefundState() == PayOrder.REFUND_STATE_ALL || payOrder.getRefundAmount() >= payOrder.getAmount()){
                throw new BizException("订单已全额退款，本次申请失败");
            }

            if(payOrder.getRefundAmount() + rq.getRefundAmount() > payOrder.getAmount()){
                throw new BizException("申请金额超出订单可退款余额，请检查退款金额");
            }

            if(refundOrderMapper.selectCount(RefundOrder.gw().eq(RefundOrder::getPayOrderId, payOrder.getPayOrderId()).eq(RefundOrder::getState, RefundOrder.STATE_ING)) > 0){
                throw new BizException("支付订单具有在途退款申请，请稍后再试");
            }

            //全部退款金额 （退款订单表）
            Long sumSuccessRefundAmount = refundOrderMapper.sumSuccessRefundAmount(payOrder.getPayOrderId());
            if(sumSuccessRefundAmount >= payOrder.getAmount()){
                throw new BizException("退款单已完成全部订单退款，本次申请失败");
            }

            if(sumSuccessRefundAmount + rq.getRefundAmount() > payOrder.getAmount()){
                throw new BizException("申请金额超出订单可退款余额，请检查退款金额");
            }

            String appId = rq.getAppId();


            //获取支付参数 (缓存数据) 和 商户信息
            MchApp mchAppExist = mchAppMapper.selectOne(new LambdaQueryWrapper<MchApp>().eq(MchApp::getAppId, appId));
            if(mchAppExist == null){
                throw new BizException("获取商户应用信息失败");
            }




            String refundOrderId = IdUtils.getId(16);//退款订单号

            //只会有一个退款订单号绑定账单号
            boolean nx = RedisUtil.setNx(CS.getCacheKeyRefundOrderId(bizBill.toString()),
                    refundOrderId,
                    -1);//30min 有效
            JSONObject jsonObject = new JSONObject();
            if (nx) {
                RefundOrder refundOrder = genRefundOrder(rq, refundOrderId, mchAppExist);

                //退款单入库 退款单状态：生成状态  此时没有和任何上游渠道产生交互。
                refundOrderMapper.insert(refundOrder);



            }

            String refundOrderIdExist = RedisUtil.getString(CS.getCacheKeyRefundOrderId(bizBill.toString()));
            jsonObject.put(CS.REFUND_ORDER_CACHE.REFUND_ORDER_ID, refundOrderIdExist);
            jsonObject.put(CS.REFUND_ORDER_CACHE.MCH_ORDER_NO, bizBill.toString());
            jsonObject.put(CS.REFUND_ORDER_CACHE.EXL_DATA, rq);
            log.info("退款订单号生成成功");

            return Result.success(jsonObject);


        } catch (Exception e) {
            RedisUtil.del(CS.getCacheKeyRefundOrderId(bizBill.toString()));
            log.error("退款订单生成错误 {}",e.getMessage());
            throw new BizException(BizStatus.REFUND_ORDER_GENERATION_FAILED.getCode(),e.getMessage());
        }
    }


    /**
     * 开始退款(全额退款,不支持部分退款)
     */
    public Result<RefundResponseV1> startRefund(String refundOrderId){
        log.info("开始处理退款订单");

        LocalDateTime refundDate = LocalDateTime.now();//退费时间
        try {
            RefundOrder refundOrder = refundOrderMapper.selectOne(RefundOrder.gw()
                    .eq(RefundOrder::getRefundOrderId,refundOrderId)
                    .in(RefundOrder::getState,Arrays.asList(RefundOrder.STATE_INIT, RefundOrder.STATE_ING)));

            if (refundOrder == null) {
                log.error("退款订单不存在 {}",refundOrderId);
                throw new BizException(BizStatus.REFUND_ORDER_NOT_EXIST);
            }


            PayOrder payOrder = payOrderService.queryMchOrder(refundOrder.getPayOrderId(), refundOrder.getMchRefundNo());


            if (payOrder == null) {
                log.error("支付订单不存在 {}",refundOrder.getPayOrderId());
                throw new BizException(BizStatus.PAY_ORDER_NOT_EXIST);
            }

            MchApp mchApp = mchAppMapper.selectOne(MchApp.gw().eq(MchApp::getAppId, payOrder.getAppId()));

            if (mchApp == null){
                log.error("商户应用不存在 {}",payOrder.getAppId());
                throw new BizException("获取商户应用信息失败");
            }

            if (payOrder.getState().equals(PayOrder.STATE_REFUND)) {
                log.error("支付订单状态异常 {}",payOrder.getState());
                throw new BizException(BizStatus.ORDER_QUERY_FAILED.getCode(),"该订单已退款");
            }


            if (!payOrder.getState().equals(PayOrder.STATE_SUCCESS)) {
                log.error("支付订单状态异常 {}",payOrder.getState());
                throw new BizException(BizStatus.ORDER_QUERY_FAILED.getCode(),"订单未支付");
            }

            AtomicInteger refundTimes = new AtomicInteger(payOrder.getRefundTimes());//退款次数

            //根据退款订单号得到订单号,查询明细,获取流水信息
            List<PayOrderFlowRecord> flowRecords = payOrderFlowRecordService.queryPayOrderFlowRecord(payOrder.getPayOrderId());

            if (CollectionUtil.isEmpty(flowRecords)) {
                log.error("订单流水信息为空");
                throw new BizException(BizStatus.ORDER_QUERY_FAILED.getCode(),"订单流水信息为空");
            }

            log.info("订单号 {},退款订单号 {}, 时间 {} , 交易流水号 {}",
                    payOrder.getPayOrderId(),refundOrder.getRefundOrderId(), LocalDateTime.now(),
                    flowRecords.stream().map(PayOrderFlowRecord::getTransRefGuid).collect(Collectors.toSet()));


            GeneralOrderDto<MyEPaymentRefundRequestV1> dto = new GeneralOrderDto<>();
            MyEPaymentRefundRequestV1 requestV1 = new MyEPaymentRefundRequestV1();
            RefundResponseV1 refundResponse = new RefundResponseV1();

            //通用参数设置
            dto.setAppId(mchApp.getAppId());
            dto.setSignSecret(mchApp.getAppSecret());
            dto.setApiGwPublicKey(mchApp.getAppPublicKey());
            dto.setAesKey(mchApp.getAppAesKey());
            dto.setIfCode("icbcpay");
            //其他参数设置
            for (PayOrderFlowRecord flowRecord : flowRecords) {
                requestV1.setCorpCis(mchApp.getMchNo());
                requestV1.setProjectId(mchApp.getProjectId());
                requestV1.setRefundSerialno(IdUtils.getId(10));//退费申请编号(必输)（随便编一个系统内唯一即可）
                requestV1.setRefundDate(refundDate.toLocalDate().toString());//退费日期（YYYY-MM-DD）(必输)（测试环境下填当前日期）
                requestV1.setOriTransSeq(flowRecord.getTransRefGuid());
                requestV1.setRefundAmount(flowRecord.getPayAmount());
                requestV1.setAuditRemark(refundOrder.getRefundReason());
                requestV1.setOriPayDate(flowRecord.getTrxDate().toLocalDate().toString());


                dto.setExaData(requestV1);

                RefundResponseV1 refundResponseV1 = icbcService.EPaymentRefundRequestV1Function(dto, payOrder.getPayOrderId());

                if (!refundResponseV1.isSuccess()) {
                    log.error("退费失败 {}",refundResponseV1.getReturnMsg());
                    throw new BizException(BizStatus.REFUND_FAILED.getCode(),refundResponseV1.getReturnMsg());
                }

                BeanUtil.copyProperties(refundResponseV1,refundResponse);

            }



            //更新退款单信息
            RefundOrder updateRefundOrder = new RefundOrder();
            updateRefundOrder.setState(RefundOrder.STATE_SUCCESS);
            updateRefundOrder.setPayAmount(payOrder.getAmount());
            updateRefundOrder.setSuccessTime(refundDate);
            refundOrderMapper.update(updateRefundOrder,RefundOrder.gw()
                    .eq(RefundOrder::getRefundOrderId,refundOrderId));


            //更新订单信息
            PayOrder updatePayOrder = new PayOrder();
            updatePayOrder.setState(PayOrder.STATE_REFUND);
            updatePayOrder.setRefundState(PayOrder.REFUND_STATE_ALL);
            updatePayOrder.setRefundAmount(refundOrder.getRefundAmount());
            updatePayOrder.setRefundTimes(refundTimes.incrementAndGet());
            payOrderService.update(updatePayOrder,PayOrder.gw().eq(PayOrder::getPayOrderId,payOrder.getPayOrderId()));


            log.info("退款完成 {}",LocalDateTime.now());



            return Result.success(refundResponse);

        }catch (Exception e) {
            log.error("处理退款订单异常 {}",e.getMessage());
            throw new BizException(BizStatus.REFUND_FAILED.getCode(),e.getMessage());
        }












    }




    /** 更新退款单为 关闭状态 **/
    public Integer updateOrderExpired(){

        RefundOrder refundOrder = new RefundOrder();
        refundOrder.setState(RefundOrder.STATE_CLOSED);

        return refundOrderMapper.update(refundOrder,
                RefundOrder.gw()
                        .in(RefundOrder::getState, Arrays.asList(RefundOrder.STATE_INIT, RefundOrder.STATE_ING))
                        .le(RefundOrder::getExpiredTime, new Date())
        );
    }
    private RefundOrder genRefundOrder(RefundOrderAdd rq, String refundOrderId , MchApp mchApp){

        RefundOrder refundOrder = new RefundOrder();
        BeanUtil.copyProperties(rq, refundOrder);

        //退款订单号设置
        refundOrder.setRefundOrderId(refundOrderId);
        //商户名称
        refundOrder.setMchName(mchApp.getAppName());
        refundOrder.setMchType(new Byte(String.valueOf(0x01)));//暂定普通商户
        refundOrder.setMchRefundNo(rq.getMchRefundNo());
        refundOrder.setCurrency(rq.getCurrency());
        refundOrder.setIfCode("ifCode");
        refundOrder.setPayAmount(rq.getPayAmount());
        refundOrder.setState(RefundOrder.STATE_INIT);
        refundOrder.setRefundReason(rq.getRefundReason());
        refundOrder.setMchNo(mchApp.getMchNo());
        refundOrder.setWayCode(rq.getWayCode());
        refundOrder.setExpiredTime(LocalDateTime.now().plusSeconds(ORDER_EXPIRED_TIME_OF_SECOND));


        return refundOrder;
    }




}
